#include <crt_util.h>

int _clock_init(_clock_t* clock)
{
    rc_error(clock != NULL, S_ERROR);

    clock->monotonic_clock.tv_sec = 0;
    clock->monotonic_clock.tv_usec = 0;

#if (TARGET_OS == OS_WIN)
    QueryPerformanceFrequency(&clock->freq);
#elif (TARGET_OS == OS_POSIX)

#ifdef SYS_CLOCK_GETTIME

#ifdef CLOCK_MONOTONIC_FAST
    clock->id = CLOCK_MONOTONIC_FAST;
#else
#if defined(CLOCK_MONOTONIC_RAW)
    clock->id = CLOCK_MONOTONIC_RAW;
#else
#if defined(CLOCK_MONOTONIC)
    clock->id = CLOCK_MONOTONIC;
#endif
#endif
#endif // CLOCK_MONOTONIC_FAST

    struct timespec ts;
    if (clock_gettime(clock->id, &ts) == -1)
        return S_ERROR;
#else

#endif

#elif (TARGET_OS == OS_MACH)

    struct mach_timebase_info mi;

    /* OSX has mach_absolute_time() */
    if (mach_timebase_info(&mi) == 0 && mach_absolute_time() != 0) {
        mi.denom *= 1000;
        memcpy(&clock->mach_timebase, &mi, sizeof(mi));
    } else {
        clock->mach_timebase.numer = 0;
        return S_ERROR;
    }

#endif
    struct timeval tv;
    get_clock(clock, &tv);
    clock->last_clock = tv;

    return S_SUCCESS;
}
long get_clock_sec(_clock_t* clock)
{
    rc_error(clock != NULL, 0);
    return clock->monotonic_clock.tv_sec;
}
long get_clock_usec(_clock_t* clock)
{
    rc_error(clock != NULL, 0);
    return clock->monotonic_clock.tv_usec;
}
int get_clock(_clock_t* clock, struct timeval* tv)
{
    rc_error(clock != NULL, S_ERROR);

#if (TARGET_OS == OS_WIN)

    int64_t first_counter = 0;

    LARGE_INTEGER end;
    QueryPerformanceCounter(&end);

    first_counter = (end.QuadPart) * 1000000 / clock->freq.QuadPart;

    tv->tv_sec = (first_counter / 1000000) & LONG_MAX;
    tv->tv_usec = (first_counter % 1000000) & LONG_MAX;

#elif (TARGET_OS == OS_POSIX)

#ifdef SYS_CLOCK_GETTIME
    struct timespec ts;
    if (clock_gettime(clock->id, &ts) == -1)
        return S_ERROR;

    tv->tv_sec = ts.tv_sec;
    tv->tv_usec = ts.tv_nsec / 1000;
#else
    gettimeofday(tv, NULL);
#endif

#elif (TARGET_OS == OS_MACH)

    uint64_t abstime, usec;
    abstime = mach_absolute_time();

    usec = (abstime * clock->mach_timebase.numer) / (clock->mach_timebase.denom);

    tv->tv_sec = usec / 1000000;
    tv->tv_usec = usec % 1000000;

#elif (TARGET_OS == OS_UNIX)

    gettimeofday(tv, NULL);

#endif

    return S_SUCCESS;
}

int clock_adjust(_clock_t* clock, struct timeval* tv)
{
    rc_error(clock != NULL, S_ERROR);

    if (timerval_cmp(tv, &clock->last_clock) == tv_cmp_great) {
        // tv  > last_clock == Under normal circumstances
        // tv - last_clock + monitonic = clock->monotonic_clock;
        // last_clock = tv + monitonic
        struct timeval sub_clock;
        timerval_sub(tv, &clock->last_clock, &sub_clock);
        timerval_add(&sub_clock, &clock->monotonic_clock, &clock->monotonic_clock);
        clock->last_clock = *tv;
    } else if (timerval_cmp(tv, &clock->last_clock) == tv_cmp_less) {
        // tv + monitonic < last_clock
        // last_clock - tv = sub_clock;
        // sub_clock +  clock->monotonic_clock = clock->monotonic_clock
        goto clearclock;
    } else if (timerval_cmp(tv, &clock->last_clock) == tv_cmp_suc)
        clock->last_clock = *tv; // I don't know. Do what
    else
        goto clearclock;

    return S_SUCCESS;
clearclock:
    clock->monotonic_clock.tv_sec = 0;
    clock->monotonic_clock.tv_usec = 0;
    clock->last_clock = *tv;
    return S_SUCCESS;
}

int clock_outputlog(_clock_t* clock)
{
    rc_error(clock != NULL, S_ERROR);

    // logi("evclock_outputlog==>sec:%ld,usec:%ld", clock->monotonic_clock.tv_sec, clock->monotonic_clock.tv_usec);

    return S_SUCCESS;
}
